package com.raft.core.rpc.nio;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.eventbus.EventBus;
import com.raft.core.node.NodeId;

import io.netty.channel.ChannelHandlerContext;

/**主体逻辑，针对入口连接，接收来自外部连接时的处理器<br/>
 * 制定一个简单的应用层协议：
 * 1、节点B向节点A发起连接后，先发送自己的nodeId
 * 2、节点A收到来自其他节点的连接后，接受的第一个消息必须是nodeId。收到nodeId后，记录在当前Channel或者Handler中。在没有收到nodeId的前提下收到其他消息，则抛异常
 * @author zhangsulei
 *
 */
class FromRemoteHandler extends AbstractHandler {

    private static final Logger logger = LoggerFactory.getLogger(FromRemoteHandler.class);
    /**入口连接组
     */
    private final InboundChannelGroup channelGroup;

    FromRemoteHandler(EventBus eventBus, InboundChannelGroup channelGroup) {
        super(eventBus);
        this.channelGroup = channelGroup;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    	// 先判断msg类型，如果是NodeId，则表示当前消息时节点标示消息，将其从Object转换为NodeId并设置到remoteId变量中，然后把当前的channel包装成NioChannel，
    	// 并加入到入口连接组InboundChannelGroup中。
    	// 如果不是NodeId消息，则调用父类的channelRead方法，它负责处理除了NodeId类型以外的消息。
    	// 这里设置remoteId的步骤并不严格，如果远程节点再次发送NodeId类型的消息，remoteId会被二次设置。一个简单的解决办法是，检查remoteId是否已经被设置，如果已经被设置则抛异常
        if (msg instanceof NodeId) {
            remoteId = (NodeId) msg;
            NioChannel nioChannel = new NioChannel(ctx.channel());
            channel = nioChannel;
            channelGroup.add(remoteId, nioChannel);
            return;
        }

        logger.debug("receive {} from {}", msg, remoteId);
        super.channelRead(ctx, msg);
    }

}
